package com.wswy.wzcx.module;


import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

import com.blankj.utilcode.util.CacheDiskUtils;
import com.che.libcommon.api.ApiOptionalResultObserver;
import com.che.libcommon.api.BaseResult;
import com.che.libcommon.utils.JsonKit;
import com.che.libcommon.utils.RxBus;
import com.che.libcommon.utils.RxHelper;
import com.che.libcommon.utils.optional.Optional;
import com.google.gson.reflect.TypeToken;
import com.wswy.wzcx.api.Api;
import com.wswy.wzcx.api.ErrorCode;
import com.wswy.wzcx.api.Gov122;
import com.wswy.wzcx.api.Gov122.GovResult;
import com.wswy.wzcx.model.UserMode;
import com.wswy.wzcx.model.gov.GovParseResp;
import com.wswy.wzcx.model.license.UserCarInfo;
import com.wswy.wzcx.model.resp.FineDataResp;
import com.wswy.wzcx.model.resp.TrafficViolationResp;

import io.reactivex.Flowable;
import io.reactivex.Single;
import io.reactivex.SingleObserver;
import io.reactivex.SingleSource;
import io.reactivex.SingleTransformer;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import io.reactivex.observers.ResourceSingleObserver;
import io.reactivex.schedulers.Schedulers;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class UserDataProvider {

    private static final String TAG = "UserDataProvider";

    private static final String KEY_MYCARS = "myCars";
    private static final String KEY_CAR_FINE_LIST = "carFineList_";

    private static final int CATCH_EXPIRED = 60 * 60 * 24;


    private CacheDiskUtils userCache;

    private CopyOnWriteArrayList<UserCarInfo> myCars = new CopyOnWriteArrayList<>();

    private static class InstanceHolder {
        private static final UserDataProvider holder = new UserDataProvider();
    }

    public static UserDataProvider get() {

        return InstanceHolder.holder;
    }


    public void init(UserMode userMode) {
        userCache = CacheDiskUtils.getInstance(userMode.mobile, 1024 * 1024, 128);
        preLoadCache();
    }


    private void preLoadCache() {
        myCarsFromCache().subscribe(new ResourceSingleObserver<Optional<List<UserCarInfo>>>() {
            @Override
            public void onSuccess(Optional<List<UserCarInfo>> listOptional) {
                myCars.clear();
                List<UserCarInfo> userCarInfos = listOptional.orNull();
                if (userCarInfos != null) {
                    myCars.addAll(userCarInfos);
                }
            }

            @Override
            public void onError(Throwable e) {

            }
        });
    }

    public void clear() {
        if (userCache != null)
            userCache.clear();
        if (myCars != null)
            myCars.clear();
        userCache = null;
    }

    private UserDataProvider() {

    }

    private Single<Optional<TrafficViolationResp>> gov122Query(final String carNo, final String engineNo, final String carType) {
        //查官网数据
        return Single.fromCallable(new Callable<Optional<TrafficViolationResp>>() {
            @Override
            public Optional<TrafficViolationResp> call() throws Exception {
                GovResult govResult = null;
                try {
                    govResult = Gov122.get122CarData(carNo, engineNo, carType);
                } catch (Throwable e) {
                    e.printStackTrace();
                }

                //有数据
                if (govResult != null) {
                    //上传数据
                    try {
                        Map<String, String> commonParams = govResult.getCommonParams();
                        if (commonParams != null) {
                            Optional<GovParseResp> govParseRespOptional = Api.get()
                                    .govQueryParse(commonParams).blockingGet();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                    //查询官网出现发动机号错误
                    if (govResult.getCode() != null && govResult.getCode() == 500) {

                        //官网提示发动机号错误
                        TrafficViolationResp fakeResp = new TrafficViolationResp();
                        fakeResp.fineData = new FineDataResp(ErrorCode.ERROR_CAR_INFO, "车牌号与发动机号不一致", null);
                        return Optional.of(fakeResp);
                    }
                }
                return Optional.absent();
            }
        }).timeout(20, TimeUnit.SECONDS, Single.just(Optional.<TrafficViolationResp>absent())).compose(RxHelper.<Optional<TrafficViolationResp>>io2main());

    }


    /**
     * 违章查询,新增或查询
     *
     * @param map
     * @return
     */
    public Single<Optional<TrafficViolationResp>> violationQuery(Map<String, String> map) {

        final String carNo = map.get("carNo");
        final String engineNo = map.get("engineNo");
        final String carType = map.get("carType");


        return Single.concat(gov122Query(carNo, engineNo, carType), Api.get().violationQuery(map)).filter(
                new Predicate<Optional<TrafficViolationResp>>() {
                    @Override
                    public boolean test(Optional<TrafficViolationResp> trafficViolationRespOptional)
                            throws Exception {
                        return trafficViolationRespOptional.isPresent();
                    }
                }).firstOrError()
                .doOnSuccess(new Consumer<Optional<TrafficViolationResp>>() {
                    @Override
                    public void accept(Optional<TrafficViolationResp> queryResultOptional) throws Exception {
                        TrafficViolationResp resp = queryResultOptional.orNull();
                        if (resp != null && resp.userCarInfo != null) {
                            updateMyCars(resp.userCarInfo);

                            //新增查询保存
                            userCache.put(KEY_CAR_FINE_LIST + resp.userCarInfo.id, JsonKit.GSON.toJson(resp), CATCH_EXPIRED);
                        }
                    }
                }).doOnSuccess(this.<Optional<TrafficViolationResp>>concatSuccess());
    }

    /**
     * 我的所有车辆
     *
     * @return
     */
    public Single<Optional<List<UserCarInfo>>> myCars() {
        return myCars(false);
    }

    /**
     * 我的所有车辆
     *
     * @param forceFromApi
     * @return
     */
    public Single<Optional<List<UserCarInfo>>> myCars(boolean forceFromApi) {
        if (userCache == null || forceFromApi) {
            return myCarsFromApi();
        }

        return Single.concat(myCarsFromCache(), myCarsFromApi()).filter(
                new Predicate<Optional<List<UserCarInfo>>>() {
                    @Override
                    public boolean test(Optional<List<UserCarInfo>> listOptional) throws Exception {
                        return listOptional.isPresent();
                    }
                }).first(Optional.<List<UserCarInfo>>absent())
                .doOnSuccess(new Consumer<Optional<List<UserCarInfo>>>() {
                    @Override
                    public void accept(Optional<List<UserCarInfo>> listOptional) throws Exception {
                        List<UserCarInfo> userCarInfos = listOptional.orNull();
                        myCars.clear();
                        if (userCarInfos != null) {
                            myCars.addAll(userCarInfos);
                        }
                    }
                });

//    return Single.concat(myCarsFromCache(),myCarsFromApi())
//            .filter(listOptional -> listOptional.orNull() != null)
//            .first(Optional.absent())
//            .doOnSuccess(listOptional ->
//            {
//              List<UserCarInfo> userCarInfos = listOptional.orNull();
//              myCars.clear();
//              if(userCarInfos != null){
//                myCars.addAll(userCarInfos);
//              }
//            });
    }

    /**
     * 根据车辆id查询违章信息
     *
     * @param forceApi
     * @param carId
     * @return
     */
    public Flowable<Optional<TrafficViolationResp>> carFineList(boolean forceApi, int carId) {
        if (forceApi || userCache == null) {
            return carFineListFromApi(carId).delay(3, TimeUnit.SECONDS).toFlowable();
        }
        return Single.concat(carFineListFromCache(carId), carFineListFromApi(carId).delay(3, TimeUnit.SECONDS));

//    return Single.concat(carFineListFromCache(carId),carFineListFromApi(carId))
//        .filter(
//        new Predicate<Optional<TrafficViolationResp>>() {
//          @Override
//          public boolean test(Optional<TrafficViolationResp> trafficViolationRespOptional)
//              throws Exception {
//            return trafficViolationRespOptional.orNull() != null;
//          }
//        }).first(Optional.<TrafficViolationResp>absent())
//        ;
    }


    /**
     * 后台根据车辆id查询违章信息
     *
     * @param carId
     */
    public void carFineListRun(final int carId) {
        carFineListFromApi(carId)
                .subscribe(new ApiOptionalResultObserver<TrafficViolationResp>() {

                    @Override
                    protected void onApiResult(@Nullable TrafficViolationResp resp) {


                    }

                    @Override
                    protected void onApiError(BaseResult result) {
                        sendUpdateError();
                        toastErrorMsg(result);
                    }


                    private void sendUpdateError() {
                        UserCarInfo userCarInfo = new UserCarInfo();
                        userCarInfo.id = carId;
                        userCarInfo.isError = true;
                        RxBus.getDefault().postWithCode(InternalMsgCode.CODE_ADD_UPDATE, userCarInfo);
                    }

                });
    }


    /**
     * 删除车辆
     *
     * @param carId
     * @return
     */
    public Single<Optional<Object>> deleteCar(final int carId) {
        return Api.get().deleteCar(carId).doOnSuccess(new Consumer<Optional<Object>>() {
            @Override
            public void accept(Optional<Object> stringOptional) throws Exception {

                delCacheCar(carId);

            }
        }).doOnSuccess(this.<Optional<Object>>concatSuccess());
    }

    private void delCacheCar(int carId) {
        if (userCache != null) {
            userCache.remove(KEY_CAR_FINE_LIST + carId);
        }

        int size = myCars.size();
        UserCarInfo delCar = null;
        for (int i = 0; i < size; i++) {
            UserCarInfo userCarInfo = myCars.get(i);
            if (userCarInfo.id == carId) {

                delCar = myCars.remove(i);
                break;
            }
        }
        saveCars();
        if (delCar != null) {
            RxBus.getDefault().postWithCode(InternalMsgCode.CODE_DELETE, delCar);
        }

    }

    /**
     * 更新车辆信息，车牌号变更时车辆id也会变
     *
     * @param carId
     * @param params
     * @return
     */
    public Single<Optional<UserCarInfo>> updateCar(final int carId, final Map<String, String> params) {
        params.put("userCarId", String.valueOf(carId));
        return Api.get().updateCar(params).doOnSuccess(new Consumer<Optional<UserCarInfo>>() {
            @Override
            public void accept(Optional<UserCarInfo> updateCarRespOptional) throws Exception {
                UserCarInfo updateCarResp = updateCarRespOptional.orNull();
                if (updateCarResp != null) {
                    if (updateCarResp.id != carId) {
                        delCacheCar(carId);
                        myCars.add(updateCarResp);
                    }
                    updateMyCars(updateCarResp);
                }

            }
        }).doOnSuccess(this.<Optional<UserCarInfo>>concatSuccess()).doOnError(
                new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        reloadMyCars();
                    }
                });
    }

    public int findCarPosition(int carId) {
        int i = 0;
        for (UserCarInfo myCar : myCars) {
            if (myCar.id == carId) {
                return i;
            }
            i++;
        }
        return myCars.size();
    }

    public boolean hasCar() {
        return !myCars.isEmpty();
    }

    public @Nullable
    UserCarInfo getUserCarInfo(int carId) {
        for (UserCarInfo myCar : myCars) {
            if (myCar.id == carId) {
                return myCar;
            }
        }
        return null;
    }

    public void updateCarExtendInfo(@NonNull UserCarInfo carInfo) {
        Single.just(carInfo)
                .subscribeOn(Schedulers.io())
                .subscribe(new SingleObserver<UserCarInfo>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onSuccess(UserCarInfo carInfo) {

                        doUpdateCarExtendInfo(carInfo);
                        RxBus.getDefault().postWithCode(InternalMsgCode.CODE_UPDATE_EXTEND, carInfo);

                    }

                    @Override
                    public void onError(Throwable e) {

                    }
                });
    }

    private void doUpdateCarExtendInfo(@NonNull UserCarInfo carInfo) {
        for (UserCarInfo myCar : myCars) {
            if (myCar.id == carInfo.id) {

                //CarExtendInfo extendInfo = myCar.carExtendInfo;
//        CarExtendInfo newInfo = carInfo.carExtendInfo;
                //extendInfo = newInfo;
//        if (extendInfo == null && newInfo != null){
//          extendInfo = newInfo;
//        }
//
//        InsuranceInfo insuranceInfo = newInfo.getInsuranceInfo();
//        Inspection inspection = newInfo.getInspection();
//
//        if (extendInfo != null){
//          extendInfo = extendInfo.copy(insuranceInfo,inspection,newInfo.getValuation(),newInfo.getCarTypeData());
//        }

                myCar.carExtendInfo = carInfo.carExtendInfo;
                saveCars();
                break;
            }
        }
    }


    private Single<Optional<TrafficViolationResp>> carFineListFromApi(int carId) {
        UserCarInfo userCarInfo = getUserCarInfo(carId);
        Single<Optional<TrafficViolationResp>> single = null;
        if (userCarInfo != null) {
            single = gov122Query(userCarInfo.carNo, userCarInfo.engineNo, userCarInfo.carType);
        } else {
            single = Single
                    .just(Optional.<TrafficViolationResp>absent());
        }

        return Single.concat(single, Api.get().carFineList(carId)).lastOrError()
                .compose(this.<TrafficViolationResp>cache(KEY_CAR_FINE_LIST + carId))
                .doOnSuccess(new Consumer<Optional<TrafficViolationResp>>() {
                    @Override
                    public void accept(Optional<TrafficViolationResp> data)
                            throws Exception {
                        TrafficViolationResp trafficViolationResp = data.orNull();
                        if (trafficViolationResp != null && trafficViolationResp.userCarInfo != null) {
                            updateMyCars(trafficViolationResp.userCarInfo);
                        }
                    }
                });
    }

    private Single<Optional<TrafficViolationResp>> carFineListFromCache(final int carId) {
        return Single.fromCallable(new Callable<Optional<TrafficViolationResp>>() {
            @Override
            public Optional<TrafficViolationResp> call() throws Exception {

                UserCarInfo userCarInfo = getUserCarInfo(carId);

                try {
                    String json = userCache.getString(KEY_CAR_FINE_LIST + carId);
                    if (!TextUtils.isEmpty(json)) {
                        TrafficViolationResp data = JsonKit.GSON.fromJson(json, TrafficViolationResp.class);
                        if (data != null) {
                            if (userCarInfo != null) {
                                //更新数据
                                if (userCarInfo.id == data.userCarInfo.id) {
                                    data.userCarInfo = userCarInfo;
                                }
                            }
                            data.fromCache = true;
                            return Optional.of(data);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                if (userCarInfo != null) {
                    TrafficViolationResp data = new TrafficViolationResp();
                    data.fromCache = true;
                    data.noCache = true;
                    data.userCarInfo = userCarInfo;
                    return Optional.of(data);
                }

                return Optional.absent();
            }
        });
    }


    private Single<Optional<List<UserCarInfo>>> myCarsFromApi() {
        return Api.get().myCars().compose(this.<List<UserCarInfo>>cache(KEY_MYCARS)).doOnSuccess(
                new Consumer<Optional<List<UserCarInfo>>>() {
                    @Override
                    public void accept(Optional<List<UserCarInfo>> listOptional) throws Exception {
                        postMyCars(listOptional.orNull());
                    }
                }).doOnError(new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
                throwable.printStackTrace();
            }
        });
    }

    private void postMyCars(List<UserCarInfo> userCarInfos) {
        myCars.clear();
        if (userCarInfos != null) {
            myCars.addAll(userCarInfos);
        }

        RxBus.getDefault().postWithCode(InternalMsgCode.CODE_UPDATE_CARS, myCars.clone());
    }

    private <T> Consumer<T> concatSuccess() {
        return new Consumer<T>() {
            @Override
            public void accept(T t) throws Exception {
                reloadMyCars();
            }
        };
    }

    private void reloadMyCars() {
        myCarsFromApi().subscribe(new SingleObserver<Optional<List<UserCarInfo>>>() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onSuccess(Optional<List<UserCarInfo>> listOptional) {
                postMyCars(listOptional.orNull());
            }

            @Override
            public void onError(Throwable e) {

            }
        });
    }

    private Single<Optional<List<UserCarInfo>>> myCarsFromCache() {
        return Single.fromCallable(new Callable<Optional<List<UserCarInfo>>>() {
            @Override
            public Optional<List<UserCarInfo>> call() throws Exception {

                return Optional.ofNullable(myCarsFromLocal());
            }
        }).compose(RxHelper.<Optional<List<UserCarInfo>>>io2main());
    }

    private <T> SingleTransformer<Optional<T>, Optional<T>> cache(final String key) {
        return new SingleTransformer<Optional<T>, Optional<T>>() {
            @Override
            public SingleSource<Optional<T>> apply(Single<Optional<T>> upstream) {
                return upstream.doOnSuccess(new Consumer<Optional<T>>() {
                    @Override
                    public void accept(Optional<T> t) throws Exception {
                        T value = t.orNull();
                        if (value != null && userCache != null) {
                            userCache.put(key, JsonKit.GSON.toJson(value), CATCH_EXPIRED);
                        }
                    }
                });
            }
        };
    }

    private void updateMyCars(UserCarInfo carInfo) {
        Single.just(carInfo)
                .subscribeOn(Schedulers.io())
                .subscribe(new SingleObserver<UserCarInfo>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onSuccess(UserCarInfo carInfo) {

                        RxBus.getDefault().postWithCode(InternalMsgCode.CODE_ADD_UPDATE, carInfo);

                        if (myCars.isEmpty()) {
                            List<UserCarInfo> infos = myCarsFromLocal();
                            if (infos != null) {
                                myCars.addAll(infos);
                            }
                        }
                        final List<UserCarInfo> userCarInfos = myCars;


                        if (userCarInfos != null) {
                            int size = userCarInfos.size();
                            boolean isUpdate = false;
                            for (int i = 0; i < size; i++) {
                                UserCarInfo userCarInfo = userCarInfos.get(i);
                                if (userCarInfo.equals(carInfo)) {
                                    userCarInfos.set(i, carInfo);
                                    isUpdate = true;
                                    break;
                                }
                            }

                            if (!isUpdate) {
                                userCarInfos.add(carInfo);
                            }

                            if (userCache != null) {
                                userCache.put(KEY_MYCARS, JsonKit.GSON.toJson(userCarInfos), CATCH_EXPIRED);
                            }
                        }

                    }

                    @Override
                    public void onError(Throwable e) {

                    }
                });

    }

    private void saveCars() {
        if (userCache != null) {
            userCache.put(KEY_MYCARS, JsonKit.GSON.toJson(myCars), CATCH_EXPIRED);
        }
    }

    private List<UserCarInfo> myCarsFromLocal() {
        try {
            String json = userCache.getString(KEY_MYCARS);
            if (!TextUtils.isEmpty(json)) {
                List<UserCarInfo> data = JsonKit.GSON.fromJson(json, new TypeToken<List<UserCarInfo>>() {
                }.getType());
                if (data != null && !data.isEmpty()) {
                    return data;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

}
